//-------------------------------------------------------------------
//	Copyright (c) 2010-2011  Zhirnov Andrey
//	This file is part of the "UXGEN_ResourceConverter" project.
//	See copyright notice in "Converter.h".
//-------------------------------------------------------------------

#include "MeshConverters.h"
#include "..\\..\\Core\\CConverter.h"
#include "md5\\md5load.h"


struct TMD5File
{
private:
	bool _AddSubMesh(TSceletalMesh &sMesh, md5_mesh_t &sMD5Mesh);
	bool _ConvertAnim(TModelInfo &sModel, TSceletalAnimData &sAnim, md5_anim_t &sMD5Anim);
	bool _ConvertBaseFrame(TModelInfo &sModel, TSceletalAnimData &sAnim, md5_model_t &sMD5Model);

public:
	TMD5File() {}

	bool LoadMesh(TModelInfo &sModel);
	bool LoadAnim(TModelInfo &sModel);
};



bool CMD5Converter::ConvertMesh(TModelInfo &sInfo)
{
	TMD5File	s_md5;

	return s_md5.LoadMesh( sInfo );
}


bool CMD5Converter::ConvertFrameAnim(TModelInfo &sInfo)
{
	return false;
}


bool CMD5Converter::ConvertSceletal(TModelInfo &sInfo)
{
	TMD5File	s_md5;

	return s_md5.LoadAnim( sInfo );
}


bool CMD5Converter::ConvertAutoType(TModelInfo &sInfo)
{
	const char	*p_ext = String::GetFileExt( sInfo.pFileName );

	if ( p_ext == nullptr )
		return false;
	
	TMD5File	s_md5;

	if ( stricmp( p_ext, "md5mesh" ) == 0 )
		return s_md5.LoadMesh( sInfo );
	else
	if ( stricmp( p_ext, "md5anim" ) == 0 )
		return s_md5.LoadAnim( sInfo );
	else
		return false;
}



bool TMD5File::_AddSubMesh(TSceletalMesh &sMesh, md5_mesh_t &sMD5Mesh)
{
#pragma pack (push, 1)
	struct TScelVertex
	{
		float	fTexCoordS,
				fTexCoordT;
		float	fWeightStart,
				fWeightCount;
		//uint	uWeightStart,
		//		uWeightCount;
	};
#pragma pack (pop)

	typedef array< TScelVertex, TFastCopyStrategy< TScelVertex > >	scelvert_array_t;

	TSceletalMesh::TSubMesh		s_submesh;

	scelvert_array_t	a_vertices;
	uint32_array_t		a_indices;

	a_vertices.Reserve( sMD5Mesh.num_verts * 3 );
	a_indices.Reserve( sMD5Mesh.num_tris * 3 );

	const uint	u_weights_offset = (uint)sMesh.aWeights.Count();

	for (int i = 0; i < sMD5Mesh.num_verts; ++i)
	{
		TScelVertex		s_scvert;
		md5_vertex_t &	s_vert	= sMD5Mesh.vertices[i];

		s_scvert.fTexCoordS		= s_vert.st[0];
		s_scvert.fTexCoordT		= s_vert.st[1];
		s_scvert.fWeightStart	= float(s_vert.start + u_weights_offset);
		s_scvert.fWeightCount	= float(s_vert.count);

		//s_scvert.uWeightStart	= s_vert.start + u_weights_offset;
		//s_scvert.uWeightCount	= s_vert.count;

		a_vertices.PushBack( s_scvert );
	}


	for (int i = 0; i < sMD5Mesh.num_weights; ++i)
	{
		TSceletalMesh::TWeight	s_scweight;
		md5_weight_t	&		s_weight	= sMD5Mesh.weights[i];

		s_scweight.fBias		= s_weight.bias;
		s_scweight.uJointIndex	= s_weight.joint;
		s_scweight.vPos = vec3( s_weight.pos[0], s_weight.pos[1], s_weight.pos[2] );

		sMesh.aWeights.PushBack( s_scweight );
	}


	for (int i = 0; i < sMD5Mesh.num_tris; ++i)
	{
		a_indices.PushBack( sMD5Mesh.triangles[i].index[0] );
		a_indices.PushBack( sMD5Mesh.triangles[i].index[1] );
		a_indices.PushBack( sMD5Mesh.triangles[i].index[2] );
	}

	s_submesh.eMode			 = gl_primitive::TRIANGLE;
	s_submesh.eType			 = gl_index::UINT;
	s_submesh.uIdxCount		 = (uint)a_indices.Count();
	s_submesh.uIdxOffset	 = (uint)sMesh.aIndices.Size();
	
	s_submesh.aAttribs.PushBack( TVertexAttrib( e_vertex_attrib::WEIGHTS,    2, sizeof(TScelVertex), sizeof(float)*2, gl_vertex::FLOAT, false, 0 ) );
	//s_submesh.aAttribs.PushBack( TVertexAttrib( e_vertex_attrib::WEIGHTS,    2, sizeof(TScelVertex), sizeof(float)*2, gl_vertex::UINT, false, 0 ) );
	s_submesh.aAttribs.PushBack( TVertexAttrib( e_vertex_attrib::TEXCOORD_0, 2, sizeof(TScelVertex), 0, gl_vertex::FLOAT, false, 0 ) );

	sMesh.aSMIndices.PushBack( sMesh.aSubMeshes.Count() );

	sMesh.aIndices.Append( a_indices.Begin(), a_indices.End() );
	sMesh.aVertices.Append( a_vertices.Begin(), a_vertices.End() );
	sMesh.aSubMeshes.PushBack( s_submesh );
	return true;
}


bool TMD5File::_ConvertAnim(TModelInfo &sModel, TSceletalAnimData &sAnim, md5_anim_t &sMD5Anim)
{
	string							s_temp;
	TSceletalAnimData::TTrackInfo	s_track;

	String::GetFileNameStr( sModel.pFileName, s_temp );
	
	s_track.sName		= s_temp;
	s_track.uFirstFrame	= (uint)sAnim.aTrackFrames.Count();
	s_track.uGroupMask	= (uint)-1;

	// add track frames
	for (int i = 0; i < sMD5Anim.num_frames; ++i)
	{
		for (int j = 0; j < sMD5Anim.num_joints; ++j)
		{
			md5_joint_t	&	s_joint = sMD5Anim.skelFrames[i][j];

			sAnim.aJoints.PushBack( TSceletalAnimData::TJoint( quat(s_joint.orient), vec3(s_joint.pos) ) );
		}

		sAnim.aTrackFrames.PushBack( i );
		sAnim.aBBoxes.PushBack( aabb_f( vec3(sMD5Anim.bboxes[i].min), vec3(sMD5Anim.bboxes[i].max) ) );
	}

	// set joint info
	for (int i = 0; i < sMD5Anim.num_joints; ++i)
	{
		md5_joint_t	&	s_joint = sMD5Anim.skelFrames[0][i];

		if ( s_joint.name[0] != 0 )
			s_joint.name[ strlen(s_joint.name)-1 ] = 0;

		sAnim.aJointInfos.PushBack( TSceletalAnimData::TJointInfo( s_joint.name+1, s_joint.parent, (uint)-1 ) );
	}

	s_track.uFramesCount = uint( sAnim.aTrackFrames.Count() - s_track.uFirstFrame );
	
	sAnim.aTracks.PushBack( s_track );
	sAnim.uFramesCount	 = sMD5Anim.num_frames;
	sAnim.uJointPerFrame = sMD5Anim.num_joints;
	sAnim.fFrameRate	 = (float)sMD5Anim.frameRate;

	return true;
}


bool TMD5File::_ConvertBaseFrame(TModelInfo &sModel, TSceletalAnimData &sAnim, md5_model_t &sMD5Model)
{
	TSceletalAnimData::TTrackInfo	s_track;

	s_track.sName		= "base";
	s_track.uFirstFrame	= (uint)sAnim.aTrackFrames.Count();
	s_track.uGroupMask	= (uint)-1;

	for (int i = 0; i < sMD5Model.num_joints; ++i)
	{
		md5_joint_t &	s_joint = sMD5Model.baseSkel[i];

		sAnim.aJoints.PushBack( TSceletalAnimData::TJoint( quat( s_joint.orient ), vec3( s_joint.pos ) ) );
	}
	
	sAnim.aTrackFrames.PushBack( 0 );
	sAnim.aBBoxes.PushBack( aabb_f() );

	sAnim.aTracks.PushBack( s_track );
	sAnim.uFramesCount		= 1;
	sAnim.uJointPerFrame	= sMD5Model.num_joints;
	sAnim.fFrameRate		= 1.f;

	return true;
}


bool TMD5File::LoadMesh(TModelInfo &sModel)
{
	md5load			c_md5;
	md5_model_t		s_model;
	const char *	p_name = String::GetFileName( sModel.pFileName );

	memset( &s_model, 0, sizeof(s_model) );

	if ( c_md5.ReadMD5Model( sModel.pFileName, &s_model ) != 1 )
		return false;
	
	sModel.sData.aMeshes.PushBack( TModelData::TModelMesh( p_name ) );
	sModel.sData.aAnimations.PushBack( TModelData::TMeshAnimInfo( p_name ) );
	
	TModelData::TModelMesh&	s_model_base = sModel.sData.aMeshes.Back();
	s_model_base.sMesh.Create( TMesh::SCELETAL_MESH );
	
	//TMeshAnimation	&		s_mesh_anim = sModel.sData.aAnimations.GetBack().sAnim;
	//s_mesh_anim.Create( TMeshAnimation::SCELETAL );


	TSceletalMesh	  &	s_mesh	= *s_model_base.sMesh.sData.pScelet;
	//TSceletalAnimData &	s_scel	= *s_mesh_anim.sData.pSceletal;
	bool				ret		= true;


	for (int i = 0; i < s_model.num_meshes; ++i)
	{
		if ( !_AddSubMesh( s_mesh, s_model.meshes[i] ) ) {
			ret = false;
			break;
		}
	}

	//ret &= _ConvertBaseFrame( sModel, s_scel, s_model );
	
	s_mesh.aLODs.PushBack( 0 );
	s_mesh.aLODs.PushBack( s_mesh.aSMIndices.Count() );

	s_mesh.uJointsPerFrame = s_model.num_joints;
	
	c_md5.FreeModel( &s_model );

	return ret;
}


bool TMD5File::LoadAnim(TModelInfo &sModel)
{
	md5load			c_md5;
	md5_anim_t		s_anim;
	const char *	p_name = String::GetFileName( sModel.pFileName );

	memset( &s_anim, 0, sizeof(s_anim) );

	if ( c_md5.ReadMD5Anim( sModel.pFileName, &s_anim ) != 1 )
		return false;

	sModel.sData.aAnimations.PushBack( TModelData::TMeshAnimInfo( p_name ) );

	TMeshAnimation	&	s_mesh_anim = sModel.sData.aAnimations.Back().sAnim;
	s_mesh_anim.Create( TMeshAnimation::SCELETAL );

	TSceletalAnimData &s_scel = *s_mesh_anim.sData.pSceletal;
	bool				ret;

	ret = _ConvertAnim( sModel, s_scel, s_anim );

	c_md5.FreeAnim( &s_anim );

	return ret;
}

